#ifndef TINX_FS_H
#define TINX_FS_H

#include <tinx/buffer.h>
#include <tinx/bitmap.h>
#include <tinx/stat.h>

#define AT_FDCWD -100

enum namei_flag
{
    NAMEI_FOLLOW = 1,
};

enum file_flag
{
    O_RDONLY = 00,      // 只读方式
    O_WRONLY = 01,      // 只写方式
    O_RDWR = 02,        // 读写方式
    O_ACCMODE = 03,     // 文件访问模式屏蔽码
    O_CREAT = 00100,    // 如果文件不存在就创建
    O_EXCL = 00200,     // 独占使用文件标志
    O_NOCTTY = 00400,   // 不分配控制终端
    O_TRUNC = 01000,    // 若文件已存在且是写操作，则长度截为 0
    O_APPEND = 02000,   // 以添加方式打开，文件指针置为文件尾
    O_NONBLOCK = 04000, // 非阻塞方式打开和操作文件
    O_CLOEXEC = 02000000
};

#define IS_SEPARATOR(ch) ((ch == '\\') || (ch == '/'))
#define ACC_MODE(x) ("\004\002\006\377"[(x) & O_ACCMODE])

enum FS_TYPE
{
    FS_TYPE_NONE,
    FS_TYPE_MINIX,
    FS_TYPE_PIPE,
    FS_TYPE_DEV,
    FS_TYPE_EXT2,
    FS_TYPE_NUM
};

typedef enum ftype_t
{
    F_ERR,
    F_FILE,
    F_DIR
} ftype_t;

typedef struct super_t
{
    void *desc;
    dev_t dev;
    int count;
    void *data;
    list_t inode_list;
    struct buffer_t *buf;
    struct fs_op_t *op;
    struct inode_t *imount;
    struct inode_t *iroot;
} super_t;

typedef struct inode_t
{
    void *desc;
    u16 nr;
    dev_t dev;
    int count;

    pid_t gid;
    uid_t uid;
    int mode;
    int nlinks;
    u32 size;
    dev_t rdev;
    time_t atime; // 访问时间
    time_t mtime; // 修改时间
    time_t ctime; // 创建时间
    u32 block_size;

    list_node_t node;
    void *data;
    struct fs_op_t *op;
    struct buffer_t *buf;
    struct task_t *wxwaiter;
    struct task_t *rxwaiter;
    struct super_t *super; // 在此超级块下
    struct super_t *mount; // 挂载到的超级块
} inode_t;

typedef struct dentry_t
{
    u32 nr;
    u32 length;
    u16 namelen;
    char name[0];
} dentry_t;

typedef struct file_t
{
    inode_t *inode; // 文件 inode
    u32 count;      // 引用计数
    idx_t offset;   // 文件偏移
    int flags;      // 文件标记
    int mode;       // 文件模式
} file_t;

typedef enum whence_t
{
    SEEK_SET,
    SEEK_CUR,
    SEEK_END
} whence_t;

typedef struct fs_op_t
{
    int (*mkfs) (int dev, int icount);

    int (*read_super) (int dev, super_t *super);
    int (*put_super) (super_t *super);

    int (*open) (inode_t *dir, char *name, int flags, int mode, inode_t **result);
    int (*close) (inode_t *inode);

    int (*read) (inode_t *inode, void *addr, int count, int offset);
    int (*write) (inode_t *inode, void *addr, int count, int offset);
    int (*readdir) (file_t *file, dentry_t *entry, int count);

    int (*lnamei) (inode_t *dir, inode_t *inode, inode_t **result);
    int (*symlink) (inode_t *dir, char *name, char *symname);
    int (*readlink) (inode_t *inode, char *buf, int size);

    int (*permission) (inode_t *inode, int mask);
    int (*namei) (inode_t *dir, char *name, char **next, inode_t **result, int flags);
    int (*mkdir) (inode_t *dir, char *name, int mode);
    int (*rmdir) (inode_t *dir, char *name);
    int (*link) (inode_t *odir, char *oldname, inode_t *ndir, char *newname);
    int (*unlink) (inode_t *dir, char *name);
    int (*mknod) (inode_t *dir, char *name, int mode, int dev);
    int (*rename) (inode_t *odir, char *oldname, inode_t *ndir, char *newname);
} fs_op_t;

typedef dentry_t dirent_t;

super_t *get_free_super();
super_t *get_super(dev_t dev);
super_t *read_super(dev_t dev);
void put_super(super_t *super);

file_t *get_file();
void put_file(file_t *file);

inode_t *nameid(char *pathname, inode_t *ipwd);
inode_t *named(char *pathname, char **next);
inode_t *namei(char *pathname);
inode_t *lnamei(char *pathname);

inode_t *get_free_inode();
inode_t *get_root_inode();
inode_t *find_inode(super_t *super, idx_t nr);
inode_t *fit_inode(inode_t *inode);
void iput(inode_t *inode);

int fs_default_nosys();

fs_op_t *fs_get_op(int type);
void fs_register_op(int type, fs_op_t *op);

int match_name(char *name, char *entry_name, char **next);

#endif